package com.kkwrite.regimen.consumer.controller.user;

import com.kkwrite.regimen.common.constant.SortTypeEnum;
import com.kkwrite.regimen.common.entity.dto.OutDTO;
import com.kkwrite.regimen.common.entity.dto.login.InviterInfoDTO;
import com.kkwrite.regimen.common.entity.dto.user.RegUserInDTO;
import com.kkwrite.regimen.common.entity.dto.user.RegUserOutDTO;
import com.kkwrite.regimen.consumer.common.AccessTokenUtil;
import com.kkwrite.regimen.consumer.common.WXApi;
import com.kkwrite.regimen.consumer.config.SystemConfig;
import com.kkwrite.regimen.consumer.service.user.UserService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.kkwrite.regimen.common.constant.Constant.BOOLEAN_STRING_FALSE;
import static com.kkwrite.regimen.common.constant.Constant.BOOLEAN_STRING_TRUE;
import static com.kkwrite.regimen.consumer.controller.constant.Constant.SERVICE_PREFIX;

/**
 * Created by lisha on 2018/7/16 16:11.
 *
 * @author lisha
 */
@RestController
@RequestMapping(value = "/user")
@Api(value = "用户模块")
public class UserController {
    @Resource(name = "userService")
    private UserService userService;
    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate redisTemplate;
    @Resource(name = "restTemplate")
    private RestTemplate restTemplate;
    @Resource(name = "accessTokenUtil")
    private AccessTokenUtil tokenUtil;
    @Resource(name = "systemConfig")
    private SystemConfig config;

    @PostMapping(value = "/bindinviter")
    @HystrixCommand(fallbackMethod = "saveInviterError")
    @ApiOperation(value = "用户绑定分享人接口")
    @ApiImplicitParam(name = "infoDTO", value = "inviteFlag、inviteCode要么全为空要么全不为空，且inviteFlag只能取值1或2",
            required = true, paramType = "body", dataType = "InviterInfoDTO")
    public OutDTO saveInviter(@RequestBody InviterInfoDTO infoDTO, @RequestHeader @ApiIgnore String token) {
        String userIdString = (String) redisTemplate.opsForHash().get(token, "userId");
        if (userIdString == null) {
            return OutDTO.of(BOOLEAN_STRING_FALSE, "002", "token invalid");
        }
        return restTemplate.postForObject(SERVICE_PREFIX + "/user/bindinviter/" + userIdString, infoDTO, OutDTO.class);
    }

    public OutDTO saveInviterError(InviterInfoDTO infoDTO, String token, Throwable t) {
        return OutDTO.of(BOOLEAN_STRING_FALSE, "绑定分享者服务异常！异常信息：" + t.getMessage(), null, infoDTO);
    }

    @PostMapping(value = "/updateinfo")
    @HystrixCommand(fallbackMethod = "updateUserError")
    @ApiOperation(value = "更新用户信息")
    public RegUserOutDTO updateUser(@RequestBody RegUserInDTO inDTO, HttpServletRequest request) {
        return userService.updateUser(inDTO, request.getHeader("token"));
    }

    public RegUserOutDTO updateUserError(RegUserInDTO inDTO, HttpServletRequest request) {
        return new RegUserOutDTO("false", "服务异常");
    }

    @GetMapping(value = "/info")
    @HystrixCommand(fallbackMethod = "queryUserInfoError")
    @ApiOperation(value = "查询用户信息", notes = "注意header中需要传入token")
    public OutDTO queryUserInfo(HttpServletRequest request) {
        String token = request.getHeader("token");
        Object userIdObj = redisTemplate.opsForHash().get(token, "userId");
        if (userIdObj == null) {
            return new OutDTO<>("false", "002", "invalid token");
        }
        return restTemplate.getForObject(SERVICE_PREFIX + "/user/getInfo/" + userIdObj, OutDTO.class);
    }

    public OutDTO queryUserInfoError(HttpServletRequest request) {
        return new OutDTO<>("false", "服务异常！", null);
    }

    @GetMapping("/queryUserByInviterId")
    @HystrixCommand(fallbackMethod = "queryUserByInviterIdError")
    @ApiOperation(value = "用户邀请明细")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", value = "获取第page页数据", required = true, allowableValues = "[0, infinity]", paramType = "query", dataType = "Long"),
            @ApiImplicitParam(name = "size", value = "每页size条记录", required = true, allowableValues = "[1, infinity]", paramType = "query", dataType = "int"),
            @ApiImplicitParam(name = "sortType", value = "按时间排序的排序类型", defaultValue = "2", allowableValues = "{1, 2}", paramType = "path", dataType = "int")})
    public OutDTO queryUserByInviterId(
            @RequestParam(required = false) Integer page,
            @RequestParam(required = false) Integer size,
            @PathVariable(required = false) Integer sortType,
            HttpServletRequest request) {
        String token = request.getHeader("token");
        Object userIdObj = redisTemplate.opsForHash().get(token, "userId");
        if (userIdObj == null) {
            return new OutDTO<>("false", "002", "token invalid");
        }
        if (page == null || page < 0 || size == null || size < 1) {
            return new OutDTO<>("false", "入参不合法");
        }
        if (!SortTypeEnum.isSortType(sortType)) {
            sortType = 2;
        }
        return restTemplate.getForObject(SERVICE_PREFIX
                        + "/user/queryUserByInviterId?page=" + page + "&size=" + size
                        + "&inviterId=" + userIdObj + "&sortType=" + sortType,
                OutDTO.class);
    }

    public OutDTO queryUserByInviterIdError(Integer page, Integer size, Integer sortType, HttpServletRequest request) {
        return new OutDTO<>("false", "服务异常！", null);
    }

    @ApiIgnore
    @GetMapping(value = "/shopgetwxqrcode")
    @HystrixCommand(fallbackMethod = "generateShopQrcodeError", commandProperties = {
            @HystrixProperty(name = "execution.timeout.enabled", value = "false")
    })
    public OutDTO generateShopQrcode(@RequestParam(required = false) String page, @RequestParam String shopId,
                                     @RequestParam Integer width) throws IOException {
        Assert.hasText(shopId, "shopId must not be null or empty");
        if (StringUtils.isBlank(page)) {
            page = "";
        }
        try {
            page = URLDecoder.decode(page, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("page URL Decoder异常");
        }
        String accessToken = tokenUtil.getAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            throw new RuntimeException("获取access_token异常！");
        }
        // yaoqingflag:1用户2店铺#yaoqingcode:用户编号或者店铺编号
        String scene = "yaoqingflag:2#yaoqingcode:" + shopId;
        String qrcodeName = generateQrcode(page, accessToken, scene, width);
        return OutDTO.of(BOOLEAN_STRING_TRUE, "OK", "OK", "/qrcode-images/" + qrcodeName);
    }

    public OutDTO generateShopQrcodeError(String page, String shopId, Integer width, Throwable t) {
        return OutDTO.of(BOOLEAN_STRING_FALSE, "生成分享二维码异常！异常信息：" + t.getMessage());
    }

    @GetMapping(value = "/getwxacodeunlimit")
    @HystrixCommand(fallbackMethod = "generateWechatCodeError", commandProperties = {
            @HystrixProperty(name = "execution.timeout.enabled", value = "false")
    })
    @ApiOperation(value = "生成用户分享二维码接口")
    @ApiImplicitParam(name = "page", value = "需要扫码打开的页面，需要url encode，如果为空默认跳主页面",
            paramType = "query", dataType = "string")
    public OutDTO generateWechatCode(@RequestParam(required = false) String page, HttpServletRequest request) throws IOException {
        String userId = (String) redisTemplate.opsForHash().get(request.getHeader("token"), "userId");
        if (userId == null) {
            return OutDTO.of(BOOLEAN_STRING_FALSE, "002", "token invalid", null);
        }
        OutDTO queryOut = restTemplate.getForObject(SERVICE_PREFIX + "/user/getInfo/" + userId, OutDTO.class);
        if (queryOut != null && queryOut.getSuccess().equals(BOOLEAN_STRING_TRUE)) {
            Map queryMap = (Map) queryOut.getData();
            String qrcodePath = (String) queryMap.get("inviteQrcodePath");
            if (!StringUtils.isBlank(qrcodePath)) {
                return OutDTO.of(BOOLEAN_STRING_TRUE, "OK", "OK", qrcodePath);
            }
        }
        if (StringUtils.isBlank(page)) {
            page = "";
        }
        try {
            page = URLDecoder.decode(page, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("page URL Decoder异常");
        }
        String accessToken = tokenUtil.getAccessToken();
        if (StringUtils.isBlank(accessToken)) {
            throw new RuntimeException("获取access_token异常！");
        }
        // yaoqingflag:1用户2店铺#yaoqingcode:用户编号或者店铺编号
        String scene = "yaoqingflag:1#yaoqingcode:" + userId;
        String qrcodeFileName = generateQrcode(page, accessToken, scene, 120);
        String accessPath = "/qrcode-images/" + qrcodeFileName;
        // 保存二维码
        RegUserInDTO inDTO = new RegUserInDTO();
        inDTO.setUserId(Integer.valueOf(userId));
        inDTO.setInviteQrcodePath(accessPath);
        OutDTO out = restTemplate.postForObject(SERVICE_PREFIX + "/user/update", inDTO, OutDTO.class);
        if (out == null || out.getSuccess().equals(BOOLEAN_STRING_FALSE)) {
            throw new RuntimeException("生成用户分享二维码保存异常");
        }
        return OutDTO.of(BOOLEAN_STRING_TRUE, "OK", "OK", accessPath);
    }

    public OutDTO generateWechatCodeError(String page, HttpServletRequest request, Throwable t) {
        return OutDTO.of(BOOLEAN_STRING_FALSE, "生成分享二维码异常！异常信息：" + t.getMessage());
    }

    private String generateQrcode(String page, String accessToken, String scene, int width) throws IOException {
        Map<String, Object> param = new HashMap<>(16);
        param.put("scene", scene);
        param.put("page", page);
        param.put("width", width);
        param.put("auto_color", false);
        Map<String, Number> lineColor = new HashMap<>(16);
        lineColor.put("r", 0);
        lineColor.put("g", 0);
        lineColor.put("b", 0);
        param.put("line_color", lineColor);
        param.put("is_hyaline", false);
        RestTemplate rest = new RestTemplate();
        ResponseEntity<org.springframework.core.io.Resource> entity = rest.postForEntity(
                WXApi.WX_CODE_UNLIMIT.replace("#ACCESS_TOKEN#", accessToken),
                param, org.springframework.core.io.Resource.class);
        if (entity == null || !entity.getStatusCode().equals(HttpStatus.OK)) {
            throw new RuntimeException("微信服务异常！");
        }
        try {
            if (!entity.hasBody() || entity.getBody().contentLength() <= 0) {
                throw new RuntimeException("调用微信接口返回值为空！");
            }
        } catch (IOException e) {
            throw new RuntimeException("解析微信接口返回异常！");
        }
        InputStream in;
        try {
            in = entity.getBody().getInputStream();
        } catch (IOException e) {
            throw new RuntimeException("获取响应流异常！");
        }
        if (entity.getHeaders().getContentType().equals(MediaType.APPLICATION_JSON_UTF8)) {
            String resultString;
            try {
                resultString = IOUtils.toString(in, StandardCharsets.UTF_8);
            } catch (IOException e) {
                throw new RuntimeException("解析响应流异常！");
            }
            throw new RuntimeException(resultString);
        }
        String qrcodeFileName = generateQrcodeFileName();
        FileUtils.copyInputStreamToFile(in, new File(config.getQrcodeFilePath() + "/" + qrcodeFileName));
        return qrcodeFileName;
    }

    @PostMapping(value = "/uploadqrcode")
    @HystrixCommand(fallbackMethod = "uploadQrcodeError")
    @ApiOperation(value = "上传分享二维码图片接口", notes = "name=picture")
    public OutDTO uploadQrcode(@ApiIgnore MultipartFile picture, HttpServletRequest request) throws IOException {
        String userId = (String) redisTemplate.opsForHash().get(request.getHeader("token"), "userId");
        if (userId == null) {
            return OutDTO.of(BOOLEAN_STRING_FALSE, "002", "token invalid");
        }
        Assert.notNull(picture, "入参不合法");
        if (picture.isEmpty()) {
            throw new IllegalArgumentException("入参不合法");
        }
        if (!request.getServletContext().getMimeType(picture.getOriginalFilename()).startsWith("image/")) {
            throw new RuntimeException("图片格式不正确！");
        }
        String qrcodeFileName = generateQrcodeFileName();
        FileUtils.copyInputStreamToFile(picture.getInputStream(),
                new File(config.getQrcodeFilePath() + "/" + qrcodeFileName));
        return OutDTO.of(BOOLEAN_STRING_TRUE, "OK", "OK", "/qrcode-images/" + qrcodeFileName);
    }

    public OutDTO uploadQrcodeError(MultipartFile picture, HttpServletRequest request, Throwable t) {
        return OutDTO.of(BOOLEAN_STRING_FALSE, "上传分享二维码图片异常，异常信息：" + t.getMessage());
    }

    private String generateQrcodeFileName() {
        int num = UUID.randomUUID().toString().hashCode();
        if (num < 0) {
            num = -num;
        }
        return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + num
                + ".png";
    }

}
